---
title: Hono on AWS with SST
description: Create and deploy a Hono API in AWS with SST.
---

There are two ways to deploy a [Hono](https://hono.dev) app to AWS with SST.

1. [Serverless](#serverless)
2. [Containers](#containers)

We'll use both to build a couple of simple apps below.

---

#### Examples

We also have a few other Hono examples that you can refer to.

- [Enabling streaming in your Hono app](/docs/examples/#aws-hono-streaming)
- [Hit counter with Redis and Hono in a container](/docs/examples/#aws-hono-container-with-redis)

---

## Serverless

We are going to build a serverless Hono API, add an S3 Bucket for file uploads, and deploy it using a Lambda function.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-hono) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our app.

```bash
npm create hono@latest aws-hono
cd aws-hono
```

We are picking the **aws-lambda** template.

##### Init SST

Now let's initialize SST in our app.

```bash
npx sst@latest init
npm install
```

Select the defaults and pick **AWS**. This'll create a `sst.config.ts` file in your project root.

---

### 2. Add an API

Let's add a Hono API using an AWS Lambda. Update your `sst.config.ts`.

```js title="sst.config.ts"
async run() {
  new sst.aws.Function("Hono", {
    url: true,
    handler: "src/index.handler",
  });
}
```

We are enabling the function URL for this.

---

##### Start dev mode

Start your app in dev mode. This runs your functions [_Live_](/docs/live/).

```bash
npx sst dev
```

This will give you the URL of your API.

```bash frame="none"
✓  Complete
   Hono: https://gyrork2ll35rsuml2yr4lifuqu0tsjft.lambda-url.us-east-1.on.aws
```

---

### 3. Add an S3 Bucket

Let's add an S3 Bucket for file uploads. Update your `sst.config.ts`.

```js title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket");
```

##### Link the bucket

Now, link the bucket to the API.

```ts title="sst.config.ts" {3}
new sst.aws.Function("Hono", {
  url: true,
  link: [bucket],
  handler: "src/index.handler",
});
```

---

### 4. Upload a file

We want the `/` route of our API to generate a pre-signed URL to upload a file to our S3 Bucket. Replace the _Hello Hono_ route in `src/index.ts`.

```ts title="src/index.ts" {4}
app.get('/', async (c) => {
  const command = new PutObjectCommand({
    Key: crypto.randomUUID(),
    Bucket: Resource.MyBucket.name,
  });

  return c.text(await getSignedUrl(s3, command));
});
```

:::tip
We are directly accessing our S3 bucket with `Resource.MyBucket.name`.
:::

Install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```

Then add the relevant imports. We'll use the extra ones below.

```ts title="src/index.ts"
import { Resource } from 'sst'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import {
  S3Client,
  GetObjectCommand,
  PutObjectCommand,
  ListObjectsV2Command,
} from '@aws-sdk/client-s3'

const s3 = new S3Client();
```

---

### 5. Download a file

We want the `/latest` route of our API to generate a pre-signed URL to download the last uploaded file in our S3 Bucket. Add this to your routes in `src/index.ts`.

```ts title="src/index.ts"
app.get('/latest', async (c) => {
  const objects = await s3.send(
    new ListObjectsV2Command({
      Bucket: Resource.MyBucket.name,
    }),
  );

  const latestFile = objects.Contents!.sort(
    (a, b) =>
      (b.LastModified?.getTime() ?? 0) - (a.LastModified?.getTime() ?? 0),
  )[0];

  const command = new GetObjectCommand({
    Key: latestFile.Key,
    Bucket: Resource.MyBucket.name,
  });

  return c.redirect(await getSignedUrl(s3, command));
});
```

---

##### Test your app

Let's try uploading a file from your project root. Make sure to use your API URL.

```bash
curl --upload-file package.json "$(curl https://gyrork2ll35rsuml2yr4lifuqu0tsjft.lambda-url.us-east-1.on.aws)"
```

Now head over to `https://gyrork2ll35rsuml2yr4lifuqu0tsjft.lambda-url.us-east-1.on.aws/latest` in your browser and it'll download the file you just uploaded.

---

### 6. Deploy your app

Now let's deploy your app.

```bash
npx sst deploy --stage production
```

You can use any stage name here but it's good to create a new stage for production.

---

## Containers

We are going to create a Hono API, add an S3 Bucket for file uploads, and deploy it in a container with the `Cluster` component.

:::tip[View source]
You can [view the source](https://github.com/sst/sst/tree/dev/examples/aws-hono-container) of this example in our repo.
:::

Before you get started, make sure to [configure your AWS credentials](/docs/iam-credentials#credentials).

---

### 1. Create a project

Let's start by creating our app.

```bash
npm create hono@latest aws-hono-container
cd aws-hono-container
```

We are picking the **nodejs** template.

##### Init SST

Now let's initialize SST in our app.

```bash
npx sst@latest init
npm install
```

Select the defaults and pick **AWS**. This'll create a `sst.config.ts` file in your project root.

---

### 2. Add a Service

To deploy our Hono app in a container, we'll use [AWS Fargate](https://aws.amazon.com/fargate/) with [Amazon ECS](https://aws.amazon.com/ecs/). Replace the `run` function in


```js title="sst.config.ts" {10-12}
async run() {
  const vpc = new sst.aws.Vpc("MyVpc");
  const cluster = new sst.aws.Cluster("MyCluster", { vpc });

  new sst.aws.Service("MyService", {
    cluster,
    loadBalancer: {
      ports: [{ listen: "80/http", forward: "3000/http" }],
    },
    dev: {
      command: "npm run dev",
    },
  });
}
```

This creates a VPC, and an ECS Cluster with a Fargate service in it.

:::note
By default, your service in not deployed when running in _dev_.
:::

The `dev.command` tells SST to instead run our Hono app locally in dev mode.

---

#### Start dev mode

Run the following to start dev mode. This'll start SST and your Hono app.

```bash
npx sst dev
```

Once complete, click on **MyService** in the sidebar and open your Hono app in your browser.

---

### 3. Add an S3 Bucket

Let's add an S3 Bucket for file uploads. Add this to your `sst.config.ts` below the `Vpc` component.

```ts title="sst.config.ts"
const bucket = new sst.aws.Bucket("MyBucket");
```

---

##### Link the bucket

Now, link the bucket to the container.

```ts title="sst.config.ts" {3}
new sst.aws.Service("MyService", {
  // ...
  link: [bucket],
});
```

This will allow us to reference the bucket in our Hono app.

---

### 4. Upload a file

We want a `POST` request made to the `/` route to upload a file to our S3 bucket. Let's add this below our _Hello Hono_ route in our `src/index.ts`.

```ts title="src/index.ts" {6}
app.post('/', async (c) => {
  const body = await c.req.parseBody();
  const file = body['file'] as File;

  const params = {
    Bucket: Resource.MyBucket.name,
    ContentType: file.type,
    Key: file.name,
    Body: file,
  };
  const upload = new Upload({
    params,
    client: s3,
  });
  await upload.done();

  return c.text('File uploaded successfully.');
});
```

Add the imports. We'll use the extra ones below.

```tsx title="src/index.ts"
import { Resource } from 'sst'
import {
  S3Client,
  GetObjectCommand,
  ListObjectsV2Command,
} from '@aws-sdk/client-s3'
import { Upload } from '@aws-sdk/lib-storage'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'

const s3 = new S3Client();
```

And install the npm packages.

```bash
npm install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner
```

---

### 5. Download the file

We'll add a `/latest` route that'll download the latest file in our S3 bucket. Let's add this below our upload route in `src/index.ts`.

```ts title="src/index.ts"
app.get('/latest', async (c) => {
  const objects = await s3.send(
    new ListObjectsV2Command({
      Bucket: Resource.MyBucket.name,
    }),
  );
  const latestFile = objects.Contents!.sort(
    (a, b) =>
      (b.LastModified?.getTime() ?? 0) - (a.LastModified?.getTime() ?? 0),
  )[0];
  const command = new GetObjectCommand({
    Key: latestFile.Key,
    Bucket: Resource.MyBucket.name,
  });
  return c.redirect(await getSignedUrl(s3, command));
});
```

---

#### Test your app

To upload a file run the following from your project root.

```bash
curl -F file=@package.json http://localhost:3000/
```

This should upload the `package.json`. Now head over to `http://localhost:3000/latest` in your browser and it'll show you what you just uploaded.

---

### 6. Deploy your app

To deploy our app we'll first add a `Dockerfile`. This is building our app by running our `build` script from above.

```diff lang="dockerfile" title="Dockerfile"
FROM node:lts-alpine AS base

FROM base AS builder
RUN apk add --no-cache gcompat
WORKDIR /app
COPY package*json tsconfig.json src ./
+ # Copy over generated types
+ COPY sst-env.d.ts* ./
RUN npm ci && \
  npm run build && \
  npm prune --production

FROM base AS runner
WORKDIR /app
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 hono
COPY --from=builder --chown=hono:nodejs /app/node_modules /app/node_modules
COPY --from=builder --chown=hono:nodejs /app/dist /app/dist
COPY --from=builder --chown=hono:nodejs /app/package.json /app/package.json

USER hono
EXPOSE 3000
CMD ["node", "/app/dist/index.js"]
```

This builds our Hono app in a Docker image.

:::tip
You need to be running [Docker Desktop](https://www.docker.com/products/docker-desktop/) to deploy your app.
:::

Let's also add a `.dockerignore` file in the root.

```bash title=".dockerignore"
node_modules
.git
```

To compile our TypeScript file, we'll need add the following to the `tsconfig.json`.

```diff lang="json" title="tsconfig.json" {4,6}
{
  "compilerOptions": {
    // ...
+    "outDir": "./dist"
  },
+  "exclude": ["node_modules"]
}
```

Install TypeScript.

```bash
npm install typescript --save-dev
```

And add a `build` script to our `package.json`.

```diff lang="json" title="package.json"
"scripts": {
  // ...
+  "build": "tsc"
}
```

Now to build our Docker image and deploy we run:

```bash
npx sst deploy --stage production
```

You can use any stage name here but it's good to create a new stage for production. This'll give the URL of your Hono app deployed as a Fargate service.

```bash
✓  Complete
   MyService: http://prod-MyServiceLoadBalanc-491430065.us-east-1.elb.amazonaws.com
```

---

## Connect the console

As a next step, you can setup the [SST Console](/docs/console/) to _**git push to deploy**_ your app and view logs from it.

![SST Console Autodeploy](../../../../../assets/docs/start/sst-console-autodeploy.png)

You can [create a free account](https://console.sst.dev) and connect it to your AWS account.
